#include <efi.h>

#include <mm/page_allocator.h>
#include <mm/paging.h>
#include <mm/heap.h>

#include <drv/serial.h>
#include <drv/acpi.h>
#include <drv/hpet.h>

#include <arch/processor.h>
#include <arch/lapic.h>
#include <arch/smp.h>
#include <arch/intr.h>

#include <task/sched.h>

#include <utils/memutils.h>
#include <utils/bitmap.h>

#include <elf.h>

uint16_t __attribute__((section(".reloc"))) __relocTable[5] = {0, 0, 0xa, 0, 0};

extern char _image_base;

static void relocate_kernel() {
	uintptr_t image_base = 0;
	Elf64_Rela *rela = NULL;

	__asm__ volatile("leaq _image_base(%%rip), %0" : "=r"(image_base));
	__asm__ volatile("leaq _rela(%%rip), %0" : "=r"(rela));

	for(uint32_t i = 0; ELF64_R_TYPE(rela[i].r_info) != R_X86_64_NONE; ++i) {
		if(ELF64_R_TYPE(rela[i].r_info) != R_X86_64_RELATIVE)
			continue;

		*(uintptr_t *)(image_base + rela[i].r_offset) += image_base;
	}
}

void *efi_get_vendor_table(efi_system_table *system_table, guid_t vendor) {
	for(uint64_t i = 0; i < system_table->number_of_configuration_table; ++i) {
		if(memcmp(&system_table->configuration_tables[i].guid, &vendor, sizeof(guid_t)) == 0)
			return system_table->configuration_tables[i].table;
	}

	return NULL;
}

EFIAPI
status_t _start(efi_handle_t image_handle, efi_system_table *system_table) {
	relocate_kernel();

	serial_init(0x3f8, 9600);

	serial_printf("kernel loaded at %p\n", &_image_base);

	mm_pallocator_init(image_handle, system_table);
	mm_paging_init();
	mm_heap_init();

	guid_t acpiv1 = EFI_ACPI_1_TABLE_GUID;
	guid_t acpiv2 = EFI_ACPI_2_TABLE_GUID;
	acpi_init(efi_get_vendor_table(system_table, acpiv1),
						efi_get_vendor_table(system_table, acpiv2));

	hpet_init();

	lapic_init();

	processor_self_init();
	intr_init();
	intr_load_idt();

	smp_init();

	task_add_test_tasks();

	lapic_start_timer();

	// for(int i = 0; i < 10; ++i) {
	// 	hpet_sleep(1000000000);

	// 	serial_printf("sched_get_tick: %u\n", sched_get_tick());
	// }

	// __asm__ volatile("cli");

	// __early_panic(0x11223344, 0x55667788);

	while(1);
}